#include "BRIC.h"
#include "ScoreComparator.h"
#include "ImageComparator.h"
#include "PriorityScorer.cpp"
#include "Configurable.h"
#include "Renderer.h"

#include <map>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <opencv/cv.h>

using namespace std;

//enum Direction { LEFT, RIGHT };

BRIC::BRIC(int width, int height, int alpha, int beta, int L) : renderer(width, height, alpha, beta) {//, Scorer* scorer) {
    this->width = width;
    this->height = height;
    this->alpha = alpha;
    this->beta = beta;
    this->L = L;
    solver.config(width, height, alpha, beta);
    scorer = new PriorityScorer();
    scorer->config(width, height, alpha, beta);
//    renderer(width, height, alpha, beta);
//    this->scorer = scorer;
//    this->scorer->config(height, width, alpha, beta);
}

void BRIC::solve(Node* root) {
    solver.solve(root);
}
void BRIC::solve(Node* root, Orientation orientation) {
    solver.solve(root, orientation);
}
void BRIC::fix(Node* root) {
    solver.fix(root);
}

void BRIC::generate_trees(Node* root, Node* paramActual, Node* leaf, vector<Node* >* trees) {
    generator.generate_trees(root, paramActual, leaf, trees);
}

double BRIC::score(Node* root) {
    return scorer->score(root);
}

bool BRIC::priority_respect(Node* root) {
    vector<Node*> leafs;
    root->get_leafs(&leafs);
    sort(leafs.begin(), leafs.end(), PriorityComparator());
    for(int i = 0; i < leafs.size() - 1; i++) {
        if(leafs.at(i)->get_image()->get_area() > leafs.at(i+1)->get_image()->get_area()) {
            return false;
        }
    }
    return true;
}

bool BRIC::bounds_respect(Node* root) {
    return root->get_width(alpha, beta) <= width && root->get_height(alpha, beta) <= height;
}

bool BRIC::valid_solve(Node* root) {
    vector<Node*> leafs;
    root->get_leafs(&leafs);
    for(int i = 0; i < leafs.size(); i++) {
        if(leafs.at(i)->get_image()->get_width() <= 0 ||
                leafs.at(i)->get_image()->get_height() <= 0)
            return false;
    }
    return true;
}

Node* BRIC::create_tree_new(vector<Image*>* _images) {
    vector<Image*> images(_images->begin(), _images->end());
    printf("creating for %d images\n", _images->size());
    map<double, vector<Image*>*> image_dic;
    vector<double> priorities;
    for(vector<Image*>::iterator it = images.begin(); it != images.end(); it++) {
        Image* image = *it;
        if(image_dic.find(image->get_relative_area()) == image_dic.end()) {
            vector<Image*>* v = new vector<Image*>();
            v->push_back(image);
            image_dic.insert(pair<double, vector<Image*>*>(image->get_relative_area(), v));
            priorities.push_back(image->get_relative_area());
        } else {
            vector<Image*>* v = image_dic.at(image->get_relative_area());
            v->push_back(image);
        }
    }
    printf("num priorities %d\n", priorities.size());
    map<double, vector<Node*>*> generated;
    int total_trees = 0;
    for(vector<double>::iterator it = priorities.begin(); it != priorities.end(); it++) {
        printf("generating for %d images\n", image_dic.at(*it)->size());
        //vector<Node*>* local_generated = generate(image_dic.at(*it));
        vector<Node*>* local_generated = new vector<Node*>();
        vector<Node*> leafs;
        for(vector<Image*>::iterator it_image = image_dic.at(*it)->begin(); it_image != image_dic.at(*it)->end(); it_image++) {
            leafs.push_back(new Node(*it_image));
        }
        generator.generate_trees(leafs, local_generated);
        printf("solving %d trees\n", local_generated->size());
        solve(local_generated);
        clean_up(local_generated);
        generated.insert(pair<double, vector<Node*>*>(*it, local_generated));
        total_trees += local_generated->size();
    }
    vector<Node*> trees;
    vector<pair<Node*, double>* > scores;
    printf("generating and scoring trees for %d nodes\n", total_trees);
    generate(&generated, &priorities, &trees);
    printf("solving %d generated trees\n", trees.size());
    for(vector<Node*>::iterator it = trees.begin(); it != trees.end(); it++) {
        Node* node = *it;
        solve(node);
//        if(node->get_area(alpha, beta) <= width * height)
        if(valid_solve(node))
            if(bounds_respect(node))
                scores.push_back(new pair<Node*, double>(*it, score(*it)));
    }
    sort(scores.begin(), scores.end(), ScoreComparator());
    pair<Node*, double>* chosen_pair = scores.back();
    Node* chosen = chosen_pair->first;
    return chosen;
}

void BRIC::create_trees(vector<Image*> images, double p, vector<Node*>* nodes) {
    map<Image*, vector<Node*>* > local_nodes;
    if(nodes->empty()) {
        for(vector<Image*>::iterator image = images.begin(); image != images.end(); image++) {
            local_nodes.insert(pair<Image*, vector<Node*>* >(*image, new vector<Node*>()));
            local_nodes.at(*image)->push_back(new Node(*image));
        }
    } else {
        for(vector<Image*>::iterator image = images.begin(); image != images.end(); image++) {
            for(vector<Node*>::iterator node = nodes->begin(); node != nodes->end(); node ++) {
                local_nodes.insert(pair<Image*, vector<Node*>* >(*image, new vector<Node*>()));
                generate_trees(*node, *node, new Node(*image), local_nodes.at(*image));
            }
        }
    }
    
}

bool first_compare(pair<int, Node*>* a, pair<int, Node*>* b) {
    return a->first < b->first;
}

void BRIC::clean_up(vector<Node*>* trees) {
    if(trees->size() > 1) {
        vector<pair<int, Node*>* > ranges;
        vector<Node*> to_remove;
        for(vector<Node*>::iterator it = trees->begin(); it != trees->end(); it++) {
            Node* n = *it;
            PriorityScorer p;
            vector<Node*> leafs;
            n->get_leafs(&leafs);
            int range = p.get_area_range(leafs);
            ranges.push_back(new pair<int, Node*>(range, n));
        }
        sort(ranges.begin(), ranges.end(), first_compare);
        int n = trees->size() / 2;
        for(int i = 0; i < n; i++) {
//            to_remove.push_back(ranges.at(i)->second);
        }
        for(vector<Node*>::iterator it = to_remove.begin(); it != to_remove.end(); it++) {
            trees->erase(it);
        }
    }
}

void BRIC::generate(map<double,vector<Node*>*>* generated, vector<double>* priorities, vector<Node*>* trees) {
    vector<double> local_priorities(priorities->begin(), priorities->end());
    double priority = local_priorities.back();
    local_priorities.pop_back();
    vector<Node*>* p_trees = generated->at(priority);
    if(trees->empty()) {
        for(vector<Node*>::iterator it = p_trees->begin(); it != p_trees->end(); it++) {
            trees->push_back(*it);
        }
        if(!local_priorities.empty())
            generate(generated, &local_priorities, trees);
    } else {
        vector<Node*> old_trees(trees->begin(), trees->end());
        trees->clear();
        for(vector<Node*>::iterator it = old_trees.begin(); it != old_trees.end(); it++) {
            for(vector<Node*>::iterator it_p = p_trees->begin(); it_p != p_trees->end(); it_p++)
                generate_trees(*it, *it, *it_p, trees);
        }
        if(!local_priorities.empty())
            generate(generated, &local_priorities, trees);
    }
}

void BRIC::solve(vector<Node*>* trees) {
    for(vector<Node*>::iterator it = trees->begin(); it != trees->end(); it++) {
        this->solve(*it);
    }
}

vector<Node*>* BRIC::generate(vector<Image*>* images) {
    vector<Image*> local_images(images->begin(), images->end());
    vector<Node*>* trees = new vector<Node*>();
    for(vector<Image*>::iterator it_image = local_images.begin(); it_image != local_images.end(); it_image++) {
        if(trees->empty()) {
            trees->push_back(new Node(*it_image));
        } else {
            vector<Node*> local_trees(trees->begin(), trees->end());
            trees->clear();
            for(vector<Node*>::iterator it_tree = local_trees.begin(); it_tree != local_trees.end(); it_tree++) {
                generate_trees(*it_tree, *it_tree, new Node(*it_image), trees);
            }            
        }
    }
    return trees;
}

Node* BRIC::create_tree(vector<Image*>* _images) {
    vector<Image*>* images = new vector<Image*>();
    for(int i = 0; i < _images->size(); i++)
        images->push_back(_images->at(i));
    sort(images->begin(), images->end(), ImageComparator());
    vector<Node*> leaf_list;
    for(int i = 0; i < images->size(); i++)
        leaf_list.push_back(new Node(images->at(i), i));
    vector<Node*> best_trees;
    for(int i = 0; i < leaf_list.size(); i++) {
        vector<Node*> trees;
        vector<pair<Node*, double>* > scores;
        if(best_trees.empty())
            generate_trees(NULL, NULL, leaf_list.at(i), &trees);
        else
            for(int j = 0; j < best_trees.size(); j++)
                generate_trees(best_trees.at(j), best_trees.at(j), leaf_list.at(i), &trees);
        for(int j = 0; j < trees.size(); j++) {
            solve(trees.at(j));
//            if(valid_solve(trees.at(j)))
                if(bounds_respect(trees.at(j)))
//                    if(priority_respect(trees.at(j)))
                        scores.push_back(new pair<Node*, double>(trees.at(j), score(trees.at(j))));
        }
        sort(scores.begin(), scores.end(), ScoreComparator());
        best_trees.clear();
        for(int j = 0; j < (int)scores.size() && (int)best_trees.size() < L; j++) {
            best_trees.push_back(scores.back()->first);
            scores.pop_back();
        }
        /*        while(!scores.empty() && (int)best_trees.size() < L) {
            best_trees.push_back(scores.back()->first);
            scores.pop_back();
        }*/
        
    }
    Node* max = best_trees.at(0);
    double max_score = score(max);
    for(int a = 1; a < best_trees.size(); a++) {
        double actual_score = score(best_trees.at(a));
        if(max_score < actual_score) {
            max_score = actual_score;
            max = best_trees.at(a);
        }
    }
    return max;
}

